/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "yaffut.h"
#include "myassert.h"

#include "TaskQueue.h"

using namespace std;
using namespace Chronos;

class TestTask : public Task {
public:
    TestTask(unsigned int i, int t, bool* d) : runCount(0), interval(i), times(t), destructCalled(d) {}
    ~TestTask() {
        *destructCalled = true;
    }
    virtual unsigned int run() {
        runCount++;
        if (runCount == times) return 0;
        else return interval;
    }

    int runCount;
    int interval;
    int times;
    bool* destructCalled;
};


struct TaskQueueTest {
	TaskQueueTest() : destructCalled1(false), destructCalled2(false), queue(0) {}
    
    bool destructCalled1;
    bool destructCalled2;
    TaskQueue queue;
};


TEST(TaskQueueTest, testShouldDestroyTasksUponDestruction) {
    {
        TaskQueue queue2(0);
        queue2.addTask(new TestTask(0, 1, &destructCalled1), 1);
        queue2.addTask(new TestTask(0, 1, &destructCalled2), 10);
        ASSERT_FALSE(destructCalled1);
        ASSERT_FALSE(destructCalled2);
    }
    ASSERT_TRUE(destructCalled1);
    ASSERT_TRUE(destructCalled2);
}


TEST(TaskQueueTest, testShouldDestroyTaskAfterRun) {
    TestTask* test1 = new TestTask(0, 1, &destructCalled1);
    queue.addTask(test1, 1);
    ASSERT_FALSE(destructCalled1);
    ASSERT_EQUAL(0, test1->runCount);

    queue.runNextTime();
    ASSERT_TRUE(destructCalled1);
}


TEST(TaskQueueTest, testShouldRunTasksInRightOrder) {
    TestTask* test1 = new TestTask(10, 2, &destructCalled1);
    TestTask* test2 = new TestTask(10, 2, &destructCalled2);
    queue.addTask(test1, 2);
    queue.addTask(test2, 1);
    ASSERT_FALSE(destructCalled1);
    ASSERT_FALSE(destructCalled2);
    ASSERT_EQUAL(0, test1->runCount);
    ASSERT_EQUAL(0, test2->runCount);

    queue.runNextTime();
    ASSERT_FALSE(destructCalled1);
    ASSERT_FALSE(destructCalled2);
    ASSERT_EQUAL(0, test1->runCount);
    ASSERT_EQUAL(1, test2->runCount);

    queue.runNextTime();
    ASSERT_FALSE(destructCalled1);
    ASSERT_FALSE(destructCalled2);
    ASSERT_EQUAL(1, test1->runCount);
    ASSERT_EQUAL(1, test2->runCount);
}


TEST(TaskQueueTest, testShouldRunTasksAtSameTime) {
    TestTask* test1 = new TestTask(10, 2, &destructCalled1);
    TestTask* test2 = new TestTask(10, 2, &destructCalled2);
    queue.addTask(test1, 2);
    queue.addTask(test2, 2);
    ASSERT_FALSE(destructCalled1);
    ASSERT_FALSE(destructCalled2);
    ASSERT_EQUAL(0, test1->runCount);
    ASSERT_EQUAL(0, test2->runCount);

    queue.runNextTime();
    ASSERT_FALSE(destructCalled1);
    ASSERT_FALSE(destructCalled2);
    ASSERT_EQUAL(0, test1->runCount);
    ASSERT_EQUAL(0, test2->runCount);

    queue.runNextTime();
    ASSERT_FALSE(destructCalled1);
    ASSERT_FALSE(destructCalled2);
    ASSERT_EQUAL(1, test1->runCount);
    ASSERT_EQUAL(1, test2->runCount);
}


TEST(TaskQueueTest, testShouldRescheduleTask) {
    TestTask* test1 = new TestTask(2, 10, &destructCalled1);
    queue.addTask(test1, 1);
    ASSERT_FALSE(destructCalled1);
    ASSERT_EQUAL(0, test1->runCount);

    for (int i=0; i<9; i++) {
        queue.runNextTime();
        ASSERT_FALSE(destructCalled1);
        ASSERT_EQUAL(i+1, test1->runCount);
        queue.runNextTime();
    }
    queue.runNextTime();
    ASSERT_TRUE(destructCalled1);
}

